package com.ruoyi.common.utils.poi;


import cn.hutool.core.lang.Tuple;
import com.ruoyi.common.utils.file.FileUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class PoiWordUtil {

//    /**
//     * 用一个docx文档作为模板，然后替换其中的内容，再写入目标文档中。
//     * @throws Exception
//     */
//    public static void templateWrite(String filePath, String outFilePath, Map<String, Object> params) throws Exception {
//        InputStream is = new FileInputStream(filePath);
//        XWPFDocument doc = new XWPFDocument(is);
//        //替换段落里面的变量
//        replaceInPara(doc, params);
//        //替换表格里面的变量
//        replaceInTable(doc, params);
//
//        OutputStream os = new FileOutputStream(outFilePath);
//        doc.write(os);
//        close(os);
//        close(is);
//    }

    /**
     * 用一个docx文档作为模板，然后替换其中的内容，再写入目标文档中。
     * isPicAppend 代表图片是替换 还是 动态追加的
     * @throws Exception
     */
    public static void writeTemplateWordToResp(String filePath,
                                               String fileName,
                                               Map<String, Object> params,
                                               HttpServletResponse response,
                                               boolean isPicAppend) throws Exception {
        InputStream is = new ClassPathResource(filePath).getInputStream();
        XWPFDocument doc = new XWPFDocument(is);
        //替换段落里面的变量
        replaceInPara(doc, params, isPicAppend);
        //渲染表格
        renderTable(doc, params);

        //文件下载
        FileUtils.setAttachmentResponseHeader(response, fileName);
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        OutputStream os = response.getOutputStream();
        doc.write(os);
        close(os);
        close(is);
    }

    private static void renderTable(XWPFDocument doc, Map<String, Object> params) {
        //我们假定以下条件
        //1.doc中仅有1个table，且该table仅有两行
        //2.该table第一行是表头，第二行是用来从params中获取value的key
        XWPFTable table = doc.getTables().get(0);
        List<Map<String,Object>> tableData = (List<Map<String,Object>>)params.get("tableData");
        //第二行保留的目的是保留样式
        //1.开始创建第三行及以后所有行
        for (int i = 0; i < tableData.size(); i++) {
            PoiWordUtil.insertRow(table, 1,2+i);
        }
        //获取第二行的key集合
        XWPFTableRow row1 = table.getRow(1);
        List<String> keyList = row1.getTableCells().stream().map(XWPFTableCell::getText).collect(Collectors.toList());

        List<XWPFTableRow> rows = table.getRows();
        for (int i = 2; i < rows.size(); i++) {
            //从第2行开始set值

            XWPFTableRow row = rows.get(i);
            Map<String,Object> columns = tableData.get(i-2); //data从0下标开始
            List<XWPFTableCell> tableCells = row.getTableCells();
            for (int j = 0; j < tableCells.size(); j++) {
                XWPFTableCell cell = tableCells.get(j);
                String key = keyList.get(j);
                cell.setText(columns.get(key).toString());
            }
        }
        //只合并第一列
        PoiWordUtil.mergeTable(table, rows.size(), 1);
        table.removeRow(1);

    }

    /**
     * 替换段落里面的书签
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInBook(XWPFParagraph paragraph, Map<String, Object> params, boolean isPicAppend) {
        if(params == null){
            return;
        }
        CTP ctp = paragraph.getCTP();
        for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
            try {
                CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
                if(!params.containsKey(bookmark.getName())) continue;
                if (isPicAppend){
                    Map<String, String> multiPicMap = (Map<String, String>)params.get(bookmark.getName());
                    multiPicMap.entrySet().forEach(entry -> {
                        String name = entry.getKey();
                        String picPath = entry.getValue();
                        InputStream picIs = null;
                        try {
                            picIs = new FileInputStream(picPath);
                            if(picIs == null) return;
                            XWPFRun picRun = paragraph.createRun();
                            //bus.png为鼠标在word里选择图片时，图片显示的名字，400，400则为像素单元，根据实际需要的大小进行调整即可。
                            int[] picWidthAndHeight = PoiWordUtil.getPictureWidthAndHeght(picPath);
                            picRun.addPicture(picIs, XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", picWidthAndHeight[0], picWidthAndHeight[1]);
                            picRun.addBreak(BreakType.TEXT_WRAPPING);

                            //添加题注,题注要先创建题注再关联到图片，非常麻烦，这里直接使用文本
                            XWPFRun txtRun = paragraph.createRun();
                            txtRun.setBold(true);
                            txtRun.setFontSize(8);
                            txtRun.setText(name);
                            txtRun.addBreak(BreakType.TEXT_WRAPPING);

                            paragraph.setAlignment(ParagraphAlignment.CENTER);

                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });

                }else {
                    String picPath = params.get(bookmark.getName()).toString();
                    InputStream picIs = new FileInputStream(picPath);
                    if(picIs == null) continue;
                    XWPFRun run = paragraph.createRun();
                    //bus.png为鼠标在word里选择图片时，图片显示的名字，400，400则为像素单元，根据实际需要的大小进行调整即可。
                    int[] picWidthAndHeight = getPictureWidthAndHeght(picPath);
                    run.addPicture(picIs, XWPFDocument.PICTURE_TYPE_PNG,"bus.png,", picWidthAndHeight[0], picWidthAndHeight[1]);
                }

            } catch (InvalidFormatException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInPara(XWPFDocument doc, Map<String, Object> params,boolean isPicAppend) {
        for(XWPFParagraph para : doc.getParagraphs()) {
            replaceInPara(para, params);
            replaceInBook(para, params,isPicAppend);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param paragraph   要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph paragraph, Map<String, Object> params) {
        if(params == null){
            return;
        }
        List<XWPFRun> runs = paragraph.getRuns();
        for (int i = 0; i < runs.size(); i++) {
            //获取字符
            String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
            if (text0 != null && text0.contains("$")) {
                //包含占位符的字符缓存
                StringBuilder cache = new StringBuilder(text0);
                int endIndex = 0;
                boolean contains = text0.contains("}");
                //同一个run中是否包含占位符
                if (!contains) {
                    int j = i + 1;
                    for (; j < runs.size(); j++) {
                        String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                        if (text1 == null) {
                            continue;
                        }
                        cache.append(text1);
                        if (text1.contains("}")) {
                            endIndex = j;
                            break;
                        }
                    }
                }
                if (contains || endIndex != 0) {
                    //处理替换
                    String s = cache.toString();
                    //System.out.println("替换字符:"+s);
                    List<String> keys = get$Value(s);
                    for (String key : keys) {
                        String value = "";
                        if(params.containsKey(key)){
                            value = params.get(key).toString();
                        }
                        String replace = s.replace("${" + key +"}", value);
                        runs.get(i).setText(replace, 0);
                        for (int j = endIndex; j > i; j--) {
                            runs.get(j).setText("", 0);
                        }
                    }

//                    for (Map.Entry<String, Object> entry : params.entrySet()) {
//                        String k = entry.getKey();
//                        String v = entry.getValue().toString();
//                        if (s.contains("${" + k +"}")) {
//                            String replace = s.replace("${" + k +"}", v);
//                            runs.get(i).setText(replace, 0);
//                            for (int j = endIndex; j > i; j--) {
//                                runs.get(j).setText("", 0);
//                            }
//
//
//                            break;
//                        }
//                    }
                }
            }
        }
    }


    /**
     * 替换表格里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
        for(XWPFTable table : doc.getTables()){
            replaceInTable(table, params);
        }
    }
    /**
     * 替换表格里面的变量
     * @param table 要替换的表格
     * @param params 参数
     */
    private static void replaceInTable(XWPFTable table, Map<String, Object> params) {
        for(XWPFTableRow row : table.getRows()){
            for(XWPFTableCell cell : row.getTableCells()){
                for(XWPFTable secTable : cell.getTables()){
                    replaceInTable(secTable,params);
                }
                for (XWPFParagraph para : cell.getParagraphs()){
                    replaceInPara(para, params);
                    replaceInBook(para, params, false);
                }
            }
        }
    }

    public static List<String> get$Value(String str) {
        List<String> variables = new ArrayList<>();
        Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            variables.add(matcher.group(1));
        }
        return variables;
    }

    /**
     * 关闭输入流
     * @param is
     */
    public static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     * @param os
     */
    public static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 计算图片宽高
     * @param picPath
     * @return
     */
    public static int[] getPictureWidthAndHeght(String picPath) {
        int[] sizeArray = new int[2];
        BufferedImage bf = null;
        try {
            bf = ImageIO.read(new File(picPath));
            int width = bf.getWidth();
            int height = bf.getHeight();
            int widthEMU = Units.toEMU(width);
            int heightEMU = Units.toEMU(height);

            if (widthEMU / 360000 > 16) {
                NumberFormat f = NumberFormat.getNumberInstance();
                f.setMaximumFractionDigits(0);
                f.setRoundingMode(RoundingMode.UP);
                //获取图片的EMU和图片在文档中最大EMU的比例值
                double d = widthEMU / (360000 * 14d);
                //根据比例值计算得出适合文档的最大EMU数值
                widthEMU = Integer.parseInt(f.format(widthEMU / d).replace(",", ""));
                heightEMU = Integer.parseInt(f.format(heightEMU / d).replace(",", ""));
                sizeArray[0] = widthEMU;
                sizeArray[1] = heightEMU;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return sizeArray;
    }


        /**
         * * insertRow 在word表格中指定位置插入一行，并将某一行的样式复制到新增行
         * @param copyRowIndex 需要复制的行位置
         * @param newRowIndex 需要新增一行的位置
         */
    public static void insertRow(XWPFTable table, int copyRowIndex, int newRowIndex) {
        // 在表格中指定的位置新增一行
        XWPFTableRow targetRow = table.insertNewTableRow(newRowIndex);
        // 获取需要复制行对象
        XWPFTableRow copyRow = table.getRow(copyRowIndex);
        //复制行对象
        targetRow.getCtRow().setTrPr(copyRow.getCtRow().getTrPr());
        //或许需要复制的行的列
        List<XWPFTableCell> copyCells = copyRow.getTableCells();
        //复制列对象
        XWPFTableCell targetCell = null;
        for (XWPFTableCell copyCell : copyCells) {
            targetCell = targetRow.addNewTableCell();
            targetCell.getCTTc().setTcPr(copyCell.getCTTc().getTcPr());
            if (copyCell.getParagraphs() != null && !copyCell.getParagraphs().isEmpty()) {
                targetCell.getParagraphs().get(0).getCTP().setPPr(copyCell.getParagraphs().get(0).getCTP().getPPr());
                if (copyCell.getParagraphs().get(0).getRuns() != null
                        && !copyCell.getParagraphs().get(0).getRuns().isEmpty()) {
                    XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
                    cellR.setBold(copyCell.getParagraphs().get(0).getRuns().get(0).isBold());
                }
            }
        }

    }

    /**
     * 合并单元格 指定列 行截止 默认起止行列 都为第0行，第0列
     *
     * @param table   table 表格
     * @param numRows 指定截止行数
     * @param numCols 指定截止列数
     *      numRows 和 numCols 只是划定了合并可以取的最大范围，具体可以参考现在的调用
     */
    public static void mergeTable(XWPFTable table, int numRows, int numCols) {

        // 创建一个二维数组来保存每个单元格的内容
        String[][] cellContents = new String[numRows][numCols];

        // 遍历每个单元格，保存内容到二维数组
        for (int row = 0; row < numRows; row++) {
            XWPFTableRow tableRow = table.getRow(row);
            List<XWPFTableCell> tableCells = tableRow.getTableCells();
            for (int col = 0; col < numCols; col++) {
                XWPFTableCell tableCell = tableCells.get(col);
                cellContents[row][col] = tableCell.getText();
            }
        }

        // 合并相同内容的单元格
        for (int row = 0; row < numRows; row++) {
            for (int col = 0; col < numCols; col++) {
                String cellContent = cellContents[row][col];
                if (cellContent != null && !cellContent.isEmpty()) {
                    int rowspan = 1;
                    int colspan = 1;

                    // 检查下一行是否有相同内容的单元格
                    for (int nextRow = row + 1; nextRow < numRows; nextRow++) {
                        if (cellContent.equals(cellContents[nextRow][col])) {
                            rowspan++;
                            // 清空下一行的内容
                            cellContents[nextRow][col] = null;
                        } else {
                            break;
                        }
                    }

                    // 检查下一列是否有相同内容的单元格
                    for (int nextCol = col + 1; nextCol < numCols; nextCol++) {
                        if (cellContent.equals(cellContents[row][nextCol])) {
                            colspan++;
                            // 清空下一列的内容
                            cellContents[row][nextCol] = null;
                        } else {
                            break;
                        }
                    }

                    // 合并单元格
                    XWPFTableCell cell = table.getRow(row).getCell(col);

                    // 设置合并单元格的属性
                    CTTcPr tcPr = cell.getCTTc().getTcPr();
                    if (tcPr == null) {
                        tcPr = cell.getCTTc().addNewTcPr();
                    }
                    tcPr.addNewVMerge().setVal(STMerge.RESTART);
                    tcPr.addNewHMerge().setVal(STMerge.RESTART);

                    // 设置合并单元格的跨度
                    for (int i = 0; i < rowspan - 1; i++) {
                        table.getRow(row + i + 1).getCell(col).getCTTc().getTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
                    }
                    for (int i = 0; i < colspan - 1; i++) {
                        table.getRow(row).getCell(col + i + 1).getCTTc().getTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
                    }
                }
            }
        }
    }




}
